/*
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * Copyright (c) 1999-2008 Apple Inc.  All Rights Reserved.
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 *
 */
 /*
	 File:       FilePrefsSource.cpp

	 Contains:   Implements object defined in FilePrefsSource.h.

	 Written by: Chris LeCroy
 */

#include "FilePrefsSource.h"
#include <string.h>
#include <stdio.h>

#include <errno.h>

#include "MyAssert.h"
#include "ConfParser.h"

const int kMaxLineLen = 2048;
const int kMaxValLen = 1024;

class KeyValuePair
{
public:

	char*   GetValue() { return fValue; }

private:
	friend class FilePrefsSource;

	KeyValuePair(const char* inKey, const char* inValue, KeyValuePair* inNext);
	~KeyValuePair();

	char* fKey;
	char* fValue;
	KeyValuePair* fNext;

	void ResetValue(const char* inValue);
};


KeyValuePair::KeyValuePair(const char* inKey, const char* inValue, KeyValuePair* inNext) :
	fKey(NULL),
	fValue(NULL),
	fNext(NULL)
{
	fKey = new char[::strlen(inKey) + 1];
	::strcpy(fKey, inKey);
	fValue = new char[::strlen(inValue) + 1];
	::strcpy(fValue, inValue);
	fNext = inNext;
}


KeyValuePair::~KeyValuePair()
{
	delete[] fKey;
	delete[] fValue;
}


void KeyValuePair::ResetValue(const char* inValue)
{
	delete[] fValue;
	fValue = new char[::strlen(inValue) + 1];
	::strcpy(fValue, inValue);
}


FilePrefsSource::FilePrefsSource(bool allowDuplicates)
	: fKeyValueList(NULL),
	fNumKeys(0),
	fAllowDuplicates(allowDuplicates)
{

}

FilePrefsSource::~FilePrefsSource()
{
	while (fKeyValueList != NULL)
	{
		KeyValuePair* keyValue = fKeyValueList;
		fKeyValueList = fKeyValueList->fNext;
		delete keyValue;
	}

}

int FilePrefsSource::GetValue(const char* inKey, char* ioValue)
{
	return (this->FindValue(inKey, ioValue) != NULL);
}


int FilePrefsSource::GetValueByIndex(const char* inKey, UInt32 inIndex, char* ioValue)
{
	KeyValuePair* thePair = this->FindValue(inKey, ioValue, inIndex);

	if (thePair == NULL)
		return false;

	return true;

	/*
	char* valuePtr = thePair->fValue;

	//this function makes the assumption that fValue doesn't start with whitespace
	Assert(*valuePtr != '\t');
	Assert(*valuePtr != ' ');

	for (UInt32 count = 0; ((count < inIndex) && (valuePtr != '\0')); count++)
	{
		//go through all the "words" on this line (delimited by whitespace)
		//until we hit the one specified by inIndex

		//we aren't at the proper word yet, so skip...
		while ((*valuePtr != ' ') && (*valuePtr != '\t') && (*valuePtr != '\0'))
			valuePtr++;

		//skip over all the whitespace between words
		while ((*valuePtr == ' ') || (*valuePtr == '\t'))
			valuePtr++;

	}

	//We've exhausted the data on this line before getting to our pref,
	//so return an error.
	if (*valuePtr == '\0')
		return false;

	//if we are here, then valuePtr is pointing to the beginning of the right word
	while ((*valuePtr != ' ') && (*valuePtr != '\t') && (*valuePtr != '\0'))
		*ioValue++ = *valuePtr++;
	*ioValue = '\0';

	return true;
	*/
}

char* FilePrefsSource::GetValueAtIndex(UInt32 inIndex)
{
	// Iterate through the queue until we have the right entry
	KeyValuePair* thePair = fKeyValueList;
	while ((thePair != NULL) && (inIndex-- > 0))
		thePair = thePair->fNext;

	if (thePair != NULL)
		return thePair->fValue;
	return NULL;
}

char* FilePrefsSource::GetKeyAtIndex(UInt32 inIndex)
{
	// Iterate through the queue until we have the right entry
	KeyValuePair* thePair = fKeyValueList;
	while ((thePair != NULL) && (inIndex-- > 0))
		thePair = thePair->fNext;

	if (thePair != NULL)
		return thePair->fKey;
	return NULL;
}

void FilePrefsSource::SetValue(const char* inKey, const char* inValue)
{
	KeyValuePair* keyValue = NULL;

	// If the key/value already exists update the value.
	// If duplicate keys are allowed, however, add a new entry regardless
	if ((!fAllowDuplicates) && ((keyValue = this->FindValue(inKey, NULL)) != NULL))
	{
		keyValue->ResetValue(inValue);
	}
	else
	{
		fKeyValueList = new KeyValuePair(inKey, inValue, fKeyValueList);
		fNumKeys++;
	}
}



bool FilePrefsSource::FilePrefsConfigSetter(const char* paramName, const char* paramValue[], void* userData)
{
	/*
		static callback routine for ParseConfigFile
	*/
	int     valueIndex = 0;

	FilePrefsSource *theFilePrefs = (FilePrefsSource*)userData;

	Assert(theFilePrefs);
	Assert(paramName);
	//  Assert(  paramValue[0] );


		// multiple values are passed in the paramValue array as distinct strs
	while (paramValue[valueIndex] != NULL)
	{
		//qtss_printf("Adding config setting  <key=\"%s\", value=\"%s\">\n", paramName,  paramValue[valueIndex] );
		theFilePrefs->SetValue(paramName, paramValue[valueIndex]);
		valueIndex++;
	}

	return false; // always succeeds
}


int FilePrefsSource::InitFromConfigFile(const char* configFilePath)
{
	/*
		load config from specified file.  return non-zero
		in the event of significant error(s).

	*/

	return ::ParseConfigFile(true, configFilePath, FilePrefsConfigSetter, this);

	/*
	int err = 0;
	char bufLine[kMaxLineLen];
	char key[kMaxValLen];
	char value[kMaxLineLen];

	FILE* fileDesc = ::fopen( configFilePath, "r");

	if (fileDesc == NULL)
	{
		// report some problem here...
		err = OSThread::GetErrno();

		Assert( err );
	}
	else
	{

		while (fgets(bufLine, sizeof(bufLine) - 1, fileDesc) != NULL)
		{
			if (bufLine[0] != '#' && bufLine[0] != '\0')
			{
				int i = 0;
				int n = 0;

				while ( bufLine[i] == ' ' || bufLine[i] == '\t')
						{ ++i;}

				n = 0;
				while ( bufLine[i] != ' ' &&
						 bufLine[i] != '\t' &&
						 bufLine[i] != '\n' &&
						 bufLine[i] != '\r' &&
						 bufLine[i] != '\0' &&
						 n < (kMaxLineLen - 1) )
				{
					key[n++] = bufLine[i++];
				}
				key[n] = '\0';

				while (bufLine[i] == ' ' || bufLine[i] == '\t')
				{++i;}

				n = 0;
				while ((bufLine[i] != '\n') && (bufLine[i] != '\0') &&
						(bufLine[i] != '\r') && (n < kMaxLineLen - 1))
				{
						  value[n++] = bufLine[i++];
				}
				value[n] = '\0';

				if (key[0] != '#' && key[0] != '\0' && value[0] != '\0')
				{
					qtss_printf("Adding config setting  <key=\"%s\", value=\"%s\">\n", key, value);
					this->SetValue(key, value);
				}
				else
				{
						//assert(false);
				}
			}
		}


		int closeErr = ::fclose(fileDesc);
		Assert(closeErr == 0);
	}

	return err;
  */


}

void FilePrefsSource::DeleteValue(const char* inKey)
{
	KeyValuePair* keyValue = fKeyValueList;
	KeyValuePair* prevKeyValue = NULL;

	while (keyValue != NULL)
	{
		if (::strcmp(inKey, keyValue->fKey) == 0)
		{
			if (prevKeyValue != NULL)
			{
				prevKeyValue->fNext = keyValue->fNext;
				delete keyValue;
			}
			else
			{
				fKeyValueList = prevKeyValue;
			}

			return;

		}
		prevKeyValue = keyValue;
		keyValue = keyValue->fNext;
	}
}


void FilePrefsSource::WriteToConfigFile(const char* configFilePath)
{
	int err = 0;
	FILE* fileDesc = ::fopen(configFilePath, "w");

	if (fileDesc != NULL)
	{
		err = ::fseek(fileDesc, 0, SEEK_END);
		Assert(err == 0);

		KeyValuePair* keyValue = fKeyValueList;

		while (keyValue != NULL)
		{
			(void)qtss_fprintf(fileDesc, "%s   %s\n\n", keyValue->fKey, keyValue->fValue);

			keyValue = keyValue->fNext;
		}

		err = ::fclose(fileDesc);
		Assert(err == 0);
	}
}


KeyValuePair* FilePrefsSource::FindValue(const char* inKey, char* ioValue, UInt32 index)
{
	KeyValuePair    *keyValue = fKeyValueList;
	UInt32          foundIndex = 0;

	if (ioValue != NULL)
		ioValue[0] = '\0';

	while (keyValue != NULL)
	{
		if (::strcmp(inKey, keyValue->fKey) == 0)
		{
			if (foundIndex == index)
			{
				if (ioValue != NULL)
					::strcpy(ioValue, keyValue->fValue);
				return keyValue;
			}
			foundIndex++;
		}
		keyValue = keyValue->fNext;
	}

	return NULL;
}
